/*
 * Copyright (c) 2017, dbcloud.org and/or its affiliates. All rights reserved.
 * Use is subject to license terms.
 *
 *
 */
package org.dbcloud.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;

/**
 * <P>
 * The object used for executing a static SQL statement and returning the
 * results it produces.
 * <P>
 * By default, only one <code>ResultSet</code> object per <code>Statement</code>
 * object can be open at the same time. Therefore, if the reading of one
 * <code>ResultSet</code> object is interleaved with the reading of another,
 * each must have been generated by different <code>Statement</code> objects.
 * All execution methods in the <code>Statement</code> interface implicitly
 * close a current <code>ResultSet</code> object of the statement if an open one
 * exists.
 *
 * @see Connection#createStatement
 * @see ResultSet
 */
public class Statement {

    private java.sql.Statement stmt;
    private Connection con = null;
    private String conString = "";

    public Statement() {
    }

    public Statement(String driverClass, String connection) throws ClassNotFoundException, SQLException {  
        try {
            conString = connection;
            Class.forName(driverClass);
            this.con = DriverManager.getConnection(connection);
            stmt = con.createStatement();
        } catch (Exception ex) {
            System.err.println(ex.toString());
            throw ex;
        }
    }

    public Statement(String driverClass, String connection, String user, String password) throws ClassNotFoundException, SQLException {
        try {
            conString = connection;
            Class.forName(driverClass);
            this.con = DriverManager.getConnection(connection, user, password);
            stmt = con.createStatement();
        } catch (Exception ex) {
            System.err.println(ex.toString());
            throw ex;
        }
    }

    public void csmakeInit(java.util.HashMap<String, String> args) throws Exception {
        if (args.containsKey("driver") && args.containsKey("connection")) {
            conString = args.get("connection");
            Class.forName(args.get("driver"));

            if (args.containsKey("user") && args.containsKey("password")) {
                this.con = DriverManager.getConnection(conString, args.get("user"), args.get("password"));
            } else {
                this.con = DriverManager.getConnection(conString);
            }
            stmt = con.createStatement();
        }
    }

    private ArrayList<Document> getColumns(ResultSet rs) throws SQLException {
        ResultSetMetaData columns = rs.getMetaData();
        int columnsCount = columns.getColumnCount();
        ArrayList<Document> header = new ArrayList();
        for (int i = 1; i <= columnsCount; i++) {
            Document doc = new Document();
            String name = columns.getColumnName(i);
            doc.append("name", name);
            String label = columns.getColumnLabel(i);
            if (label != null && !label.isEmpty()) {
                doc.append("label", label);
            }
            String className = columns.getColumnClassName(i);
            if (className != null && !className.isEmpty()) {
                doc.append("className", className);
            }
            String typeName = columns.getColumnTypeName(i);
            if (typeName != null && !typeName.isEmpty()) {
                doc.append("typeName", typeName);
            }
            String schemaName = columns.getSchemaName(i);
            if (schemaName != null && !schemaName.isEmpty()) {
                doc.append("schemaName", schemaName);
            }
            String tableName = columns.getTableName(i);
            if (tableName != null && !tableName.isEmpty()) {
                doc.append("tableName", tableName);
            }
            header.add(doc);
        }
        return header;
    }

    private org.dbcloud.jdbc.ResultSet convResultSet(ResultSet rs, org.dbcloud.jdbc.ResultSet result, Document options) throws SQLException {
        if (rs != null) {
            if (options.getBoolean("document", false)) {
                ArrayList<Object> list = new ArrayList();
                ResultSetMetaData columns = rs.getMetaData();
                result.columns=getColumns(rs);
                int columnsCount = columns.getColumnCount();
                while (rs.next()) {
                    Document doc = new Document();
                    // read the result set
                    for (int i = 1; i <= columnsCount; i++) {
                        String name = columns.getColumnName(i);
                        doc.append(name, rs.getObject(i));
                    }
                    list.add(doc);
                }
                result.rows= list;
            } else {
                ArrayList columns = getColumns(rs);
                int columnsCount = columns.size();
                result.columns= columns;
                ArrayList<Object> rows = new ArrayList();
                while (rs.next()) {

                    // read the result set
                    ArrayList<Object> cells = new ArrayList();
                    for (int i = 1; i <= columnsCount; i++) {
                        cells.add(rs.getObject(i));
                    }
                    rows.add(cells);
                }
                result.rows=rows;
            }
            rs.close();
            rs = null;
        }
        return result;
    }

    /**
     * Executes the given SQL statement, which returns a single
     * <code>ResultSet</code> object.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql an SQL statement to be sent to the database, typically a
     * static SQL <code>SELECT</code> statement
     * @return a <code>ResultSet</code> object that contains the data produced
     * by the given query; never <code>null</code>
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the given SQL statement
     * produces anything other than a single <code>ResultSet</code> object, the
     * method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     */
    public org.dbcloud.jdbc.ResultSet executeQuery(String sql) {
        org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
        ResultSet rs = null;
        try {
            rs = stmt.executeQuery(sql);
            if (rs != null) {
                convResultSet(rs, result, new Document());
                rs = null;
            }
        } catch (SQLException e) {
            // if the error message is "out of memory", 
            // it probably means no database file is found
            result.error= new Document("message", e.getMessage()).append("code", e.getErrorCode()).append("SQLState", e.getSQLState());
            System.err.println(e.getMessage());
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                // connection close failed.
                System.err.println(e);
            }
        }
        return result;
    }

    public org.dbcloud.jdbc.ResultSet executeQuery(String sql, Document options) {
        if (options.getBoolean("document", false)) {
            ResultSet rs = null;
            org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
            try {
                ArrayList<Document> list = new ArrayList();
                rs = stmt.executeQuery(sql);
                if (rs != null) {
                    convResultSet(rs, result, options);
                    rs = null;
                }
            } catch (SQLException e) {
                // if the error message is "out of memory", 
                // it probably means no database file is found
                System.err.println(e.getMessage());
                result.error=new Document("message", e.getMessage()).append("code", e.getErrorCode()).append("SQLState", e.getSQLState());
            } finally {
                try {
                    if (rs != null) {
                        rs.close();
                    }
                } catch (SQLException e) {
                    // connection close failed.
                    System.err.println(e);
                }
            }
            return result;
        } else {
            return executeQuery(sql);
        }
    }

    /**
     * 执行存储过程
     *
     * @param procedureName 存储过程名称
     * @param parameters 存储过程参数 Document{ParameterName: ,Document{Direction:
     * ,SqlType: ,Value: ,OutValue: }}
     * ParameterName:参数名称，Direction：标识输入输出参数(in,out),SqlType:参数类型(java.sql.Types)
     * Value:参数值，OutValue:输出参数值
     * @param options {"document": (true or false)}"document:true"返回结果中"rows":[{
     * ... },{...}],"document:false"返回结果中"rows":[[...],[...]]
     * @return 返回Document内容：存储过程返回的结果
     */
    public org.dbcloud.jdbc.ResultSet executeProcedure(String procedureName, Document parameters, Document options) {

        ResultSet rs = null;
        org.dbcloud.jdbc.ResultSet resultDocument = new org.dbcloud.jdbc.ResultSet();
        java.sql.CallableStatement callableStatement = null;
        String callString = null;

        try {
            int parameterCount = 0;
            if (parameters != null) {
                parameterCount = parameters.size();
            }
            // <editor-fold desc="构造callString">
            StringBuilder callStringBuilder = null;
            if (parameterCount > 0) {
                callStringBuilder = new StringBuilder();
                for (int i = 0; i < parameterCount; i++) {
                    if (callStringBuilder.length() == 0) {
                        callStringBuilder.append("?");
                    } else {
                        callStringBuilder.append(",?");
                    }
                }
            }
            if (callStringBuilder != null) {
                callString = "{call " + procedureName + "(" + callStringBuilder.toString() + ")}";
            } else {
                callString = "{call " + procedureName + " }";
            }

            // </editor-fold>
            callableStatement = this.con.prepareCall(callString);

            // <editor-fold desc="向callableStatement设置参数值">
            int i = 0;
            for (String parameterName : parameters.keySet()) {
                Document parameter = parameters.get(parameterName, Document.class);
                String parameterDirection = parameter.getString("Direction"); //in or out
                if (parameterDirection == "out") {
                    callableStatement.registerOutParameter(i + 1, parameter.getInteger("SqlType"));
                } else {
                    int sqlType = parameter.getInteger("SqlType");

                    if (sqlType == java.sql.Types.INTEGER) {
                        callableStatement.setInt(i + 1, parameter.getInteger("Value"));
                    } else if (sqlType == java.sql.Types.BIGINT) {
                        callableStatement.setLong(i + 1, parameter.getLong("Value"));
                    } else if (sqlType == java.sql.Types.NVARCHAR || sqlType == java.sql.Types.VARCHAR) {
                        callableStatement.setString(i + 1, parameter.getString("Value"));
                    } else if (sqlType == java.sql.Types.DOUBLE) {
                        callableStatement.setDouble(i + 1, parameter.getDouble("Value"));
                    } else if (sqlType == java.sql.Types.TIMESTAMP) {
                        try {
                            callableStatement.setTimestamp(i + 1, parameter.get("Value", java.sql.Timestamp.class)); //需要测试 
                        } catch (Exception castExcep) {
                            String exString = castExcep.toString();
                        }
                    } else if (sqlType == java.sql.Types.BOOLEAN) {
                        callableStatement.setBoolean(i + 1, parameter.getBoolean("Value"));
                    }
                }
                i++;
            }
            // </editor-fold>

            callableStatement.execute();

            // <editor-fold desc="处理输出参数">
            i = 0;
            for (String parameterName : parameters.keySet()) {
                Document parameter = parameters.get(parameterName, Document.class);
                String parameterDirection = parameter.getString("Direction"); //in or out
                int sqlType = parameter.getInteger("SqlType");
                if (parameterDirection == "out") {
                    if (sqlType == java.sql.Types.INTEGER) {
                        parameter.put("OutValue", callableStatement.getInt(i + 1));
                    } else if (sqlType == java.sql.Types.BIGINT) {
                        parameter.put("OutValue", callableStatement.getLong(i + 1));
                    } else if (sqlType == java.sql.Types.DOUBLE) {
                        parameter.put("OutValue", callableStatement.getDouble(i + 1));
                    } else if (sqlType == java.sql.Types.FLOAT) {
                        parameter.put("OutValue", callableStatement.getFloat(i + 1));
                    } else if (sqlType == java.sql.Types.VARCHAR || sqlType == java.sql.Types.NVARCHAR) {
                        parameter.put("OutValue", callableStatement.getString(i + 1));
                    } else if (sqlType == java.sql.Types.BOOLEAN) {
                        parameter.put("OutValue", callableStatement.getBoolean(i + 1));
                    }
                }
                i++;
            }

            // </editor-fold>
            rs = callableStatement.getResultSet();
            if (rs != null) {
                convResultSet(rs, resultDocument, options);
                rs.close();
                rs = null;
            }
        } catch (Exception e) {
            // <editor-fold desc="异常处理">
            String strLog = "execCSTMTNonQuery" + callString;
            int i = 0;
            for (String parameterName : parameters.keySet()) {
                Document parameter = parameters.get(parameterName, Document.class);
                String parameterDirection = parameter.getString("Direction"); //in or out
                int sqlType = parameter.getInteger("SqlType");

                strLog = strLog + "\r\n????" + (i + 1);

                strLog = strLog + "inout=" + parameterDirection;
                strLog = strLog + "parameterType=" + sqlType;
                strLog = strLog + "in=" + parameter.get("Value");
                strLog = strLog + " out=" + parameter.get("OutValue");

                i++;
            }
            // </editor-fold>
        } finally {
            // <editor-fold desc="释放资源">
            try {
                callableStatement.close();
            } catch (Exception ex) {
                //log.error("CLOSE CALLABLESTAMENT ", ex);  
            }
            callableStatement = null;
            // </editor-fold>
        }
        return resultDocument;
    }

    /**
     * Executes the given SQL statement, which may be an <code>INSERT</code>,
     * <code>UPDATE</code>, or <code>DELETE</code> statement or an SQL statement
     * that returns nothing, such as an SQL DDL statement.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the given SQL statement
     * produces a <code>ResultSet</code> object, the method is called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     */
    public int executeUpdate(String sql) throws SQLException {
        return stmt.executeUpdate(sql);
    }

    public int[] executeUpdate(String[] sqls) throws SQLException {
        for (String sql : sqls) {
            stmt.addBatch(sql);
        }
        return stmt.executeBatch();
    }

    /**
     * Releases this <code>Statement</code> object's database and Statement
     * resources immediately instead of waiting for this to happen when it is
     * automatically closed. It is generally good practice to release resources
     * as soon as you are finished with them to avoid tying up database
     * resources.
     * <P>
     * Calling the method <code>close</code> on a <code>Statement</code> object
     * that is already closed has no effect.
     * <P>
     * <B>Note:</B>When a <code>Statement</code> object is closed, its current
     * <code>ResultSet</code> object, if one exists, is also closed.
     *
     * @exception SQLException if a database access error occurs
     */
    public void close() throws SQLException {
        stmt.close();
        con.close();
    }

    //----------------------------------------------------------------------
    /**
     * Retrieves the maximum number of bytes that can be returned for character
     * and binary column values in a <code>ResultSet</code> object produced by
     * this <code>Statement</code> object. This limit applies only to
     * <code>BINARY</code>, <code>VARBINARY</code>, <code>LONGVARBINARY</code>,
     * <code>CHAR</code>, <code>VARCHAR</code>, <code>NCHAR</code>,
     * <code>NVARCHAR</code>, <code>LONGNVARCHAR</code> and
     * <code>LONGVARCHAR</code> columns. If the limit is exceeded, the excess
     * data is silently discarded.
     *
     * @return the current column size limit for columns storing character and
     * binary values; zero means there is no limit
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #setMaxFieldSize
     */
    public int getMaxFieldSize() throws SQLException {
        return stmt.getMaxFieldSize();
    }

    /**
     * Sets the limit for the maximum number of bytes that can be returned for
     * character and binary column values in a <code>ResultSet</code> object
     * produced by this <code>Statement</code> object.
     *
     * This limit applies only to <code>BINARY</code>, <code>VARBINARY</code>,
     * <code>LONGVARBINARY</code>, <code>CHAR</code>, <code>VARCHAR</code>,
     * <code>NCHAR</code>, <code>NVARCHAR</code>, <code>LONGNVARCHAR</code> and
     * <code>LONGVARCHAR</code> fields. If the limit is exceeded, the excess
     * data is silently discarded. For maximum portability, use values greater
     * than 256.
     *
     * @param max the new column size limit in bytes; zero means there is no
     * limit
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the condition
     * {@code max >= 0} is not satisfied
     * @see #getMaxFieldSize
     */
    public void setMaxFieldSize(int max) throws SQLException {
        stmt.setMaxFieldSize(max);
    }

    /**
     * Retrieves the maximum number of rows that a <code>ResultSet</code> object
     * produced by this <code>Statement</code> object can contain. If this limit
     * is exceeded, the excess rows are silently dropped.
     *
     * @return the current maximum number of rows for a <code>ResultSet</code>
     * object produced by this <code>Statement</code> object; zero means there
     * is no limit
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #setMaxRows
     */
    public int getMaxRows() throws SQLException {
        return stmt.getMaxRows();
    }

    /**
     * Sets the limit for the maximum number of rows that any
     * <code>ResultSet</code> object generated by this <code>Statement</code>
     * object can contain to the given number. If the limit is exceeded, the
     * excess rows are silently dropped.
     *
     * @param max the new max rows limit; zero means there is no limit
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the condition
     * {@code max >= 0} is not satisfied
     * @see #getMaxRows
     */
    public void setMaxRows(int max) throws SQLException {
        stmt.setMaxRows(max);
    }

    /**
     * Sets escape processing on or off. If escape scanning is on (the default),
     * the driver will do escape substitution before sending the SQL statement
     * to the database.
     * <p>
     * The {@code Connection} and {@code DataSource} property
     * {@code escapeProcessing} may be used to change the default escape
     * processing behavior. A value of true (the default) enables escape
     * Processing for all {@code Statement} objects. A value of false disables
     * escape processing for all {@code Statement} objects. The
     * {@code setEscapeProcessing} method may be used to specify the escape
     * processing behavior for an individual {@code Statement} object.
     * <p>
     * Note: Since prepared statements have usually been parsed prior to making
     * this call, disabling escape processing for
     * <code>PreparedStatements</code> objects will have no effect.
     *
     * @param enable <code>true</code> to enable escape processing;
     * <code>false</code> to disable it
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     */
    public void setEscapeProcessing(boolean enable) throws SQLException {
        stmt.setEscapeProcessing(enable);
    }

    /**
     * Retrieves the number of seconds the driver will wait for a
     * <code>Statement</code> object to execute. If the limit is exceeded, a
     * <code>SQLException</code> is thrown.
     *
     * @return the current query timeout limit in seconds; zero means there is
     * no limit
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #setQueryTimeout
     */
    public int getQueryTimeout() throws SQLException {
        return stmt.getQueryTimeout();
    }

    /**
     * Sets the number of seconds the driver will wait for a
     * <code>Statement</code> object to execute to the given number of seconds.
     * By default there is no limit on the amount of time allowed for a running
     * statement to complete. If the limit is exceeded, an
     * <code>SQLTimeoutException</code> is thrown. A Statement driver must apply
     * this limit to the <code>execute</code>, <code>executeQuery</code> and
     * <code>executeUpdate</code> methods.
     * <p>
     * <strong>Note:</strong> Statement driver implementations may also apply
     * this limit to {@code ResultSet} methods (consult your driver vendor
     * documentation for details).
     * <p>
     * <strong>Note:</strong> In the case of {@code Statement} batching, it is
     * implementation defined as to whether the time-out is applied to
     * individual SQL commands added via the {@code addBatch} method or to the
     * entire batch of SQL commands invoked by the {@code executeBatch} method
     * (consult your driver vendor documentation for details).
     *
     * @param seconds the new query timeout limit in seconds; zero means there
     * is no limit
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the condition
     * {@code seconds >= 0} is not satisfied
     * @see #getQueryTimeout
     */
    public void setQueryTimeout(int seconds) throws SQLException {
        stmt.setQueryTimeout(seconds);
    }

    /**
     * Cancels this <code>Statement</code> object if both the DBMS and driver
     * support aborting an SQL statement. This method can be used by one thread
     * to cancel a statement that is being executed by another thread.
     *
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     */
    public void cancel() throws SQLException {
        stmt.cancel();
    }

    /**
     * Retrieves the first warning reported by calls on this
     * <code>Statement</code> object. Subsequent <code>Statement</code> object
     * warnings will be chained to this <code>SQLWarning</code> object.
     *
     * <p>
     * The warning chain is automatically cleared each time a statement is
     * (re)executed. This method may not be called on a closed
     * <code>Statement</code> object; doing so will cause an
     * <code>SQLException</code> to be thrown.
     *
     * <P>
     * <B>Note:</B> If you are processing a <code>ResultSet</code> object, any
     * warnings associated with reads on that <code>ResultSet</code> object will
     * be chained on it rather than on the <code>Statement</code> object that
     * produced it.
     *
     * @return the first <code>SQLWarning</code> object or <code>null</code> if
     * there are no warnings
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     */
    SQLWarning getWarnings() throws SQLException {
        return stmt.getWarnings();
    }

    /**
     * Clears all the warnings reported on this <code>Statement</code> object.
     * After a call to this method, the method <code>getWarnings</code> will
     * return <code>null</code> until a new warning is reported for this
     * <code>Statement</code> object.
     *
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     */
    public void clearWarnings() throws SQLException {
        stmt.clearWarnings();
    }

    /**
     * Sets the SQL cursor name to the given <code>String</code>, which will be
     * used by subsequent <code>Statement</code> object <code>execute</code>
     * methods. This name can then be used in SQL positioned update or delete
     * statements to identify the current row in the <code>ResultSet</code>
     * object generated by this statement. If the database does not support
     * positioned update/delete, this method is a noop. To insure that a cursor
     * has the proper isolation level to support updates, the cursor's
     * <code>SELECT</code> statement should have the form
     * <code>SELECT FOR UPDATE</code>. If <code>FOR UPDATE</code> is not
     * present, positioned updates may fail.
     *
     * <P>
     * <B>Note:</B> By definition, the execution of positioned updates and
     * deletes must be done by a different <code>Statement</code> object than
     * the one that generated the <code>ResultSet</code> object being used for
     * positioning. Also, cursor names must be unique within a connection.
     *
     * @param name the new cursor name, which must be unique within a connection
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     */
    public void setCursorName(String name) throws SQLException {
        stmt.setCursorName(name);
    }

    //----------------------- Multiple Results --------------------------
    /**
     * Executes the given SQL statement, which may return multiple results. In
     * some (uncommon) situations, a single SQL statement may return multiple
     * result sets and/or update counts. Normally you can ignore this unless you
     * are (1) executing a stored procedure that you know may return multiple
     * results or (2) you are dynamically executing an unknown SQL string.
     * <P>
     * The <code>execute</code> method executes an SQL statement and indicates
     * the form of the first result. You must then use the methods
     * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve the
     * result, and <code>getMoreResults</code> to move to any subsequent
     * result(s).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql any SQL statement
     * @return <code>true</code> if the first result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no
     * results
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the method is called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @see #getResultSet
     * @see #getUpdateCount
     * @see #getMoreResults
     */
    public boolean execute(String sql) throws SQLException {
        return stmt.execute(sql);
    }

    public int[] execute(String[] sqls) throws SQLException {
        stmt.clearBatch();
        for (String sql : sqls) {
            stmt.addBatch(sql);
        }
        return stmt.executeBatch();
    }

    /**
     * Retrieves the current result as a <code>ResultSet</code> object. This
     * method should be called only once per result.
     *
     * @return the current result as a <code>ResultSet</code> object or
     * <code>null</code> if the result is an update count or there are no more
     * results
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #execute
     */
    public org.dbcloud.jdbc.ResultSet getResultSet() throws SQLException {
        org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
        convResultSet(stmt.getResultSet(), result, new Document());
        return result;
    }

    public org.dbcloud.jdbc.ResultSet getResultSet(Document options) throws SQLException {
        org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
        convResultSet(stmt.getResultSet(), result, options);
        return result;
    }

    /**
     * Retrieves the current result as an update count; if the result is a
     * <code>ResultSet</code> object or there are no more results, -1 is
     * returned. This method should be called only once per result.
     *
     * @return the current result as an update count; -1 if the current result
     * is a <code>ResultSet</code> object or there are no more results
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #execute
     */
    public int getUpdateCount() throws SQLException {
        return stmt.getUpdateCount();
    }

    /**
     * Moves to this <code>Statement</code> object's next result, returns
     * <code>true</code> if it is a <code>ResultSet</code> object, and
     * implicitly closes any current <code>ResultSet</code> object(s) obtained
     * with the method <code>getResultSet</code>.
     *
     * <P>
     * There are no more results when the following is true:
     * <PRE>{@code
     *     // stmt is a Statement object
     *     ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
     * }</PRE>
     *
     * @return <code>true</code> if the next result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no more
     * results
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #execute
     */
    public boolean getMoreResults() throws SQLException {
        return stmt.getMoreResults();
    }

    //--------------------------Statement 2.0-----------------------------
    /**
     * Gives the driver a hint as to the direction in which rows will be
     * processed in <code>ResultSet</code> objects created using this
     * <code>Statement</code> object. The default value is
     * <code>ResultSet.FETCH_FORWARD</code>.
     * <P>
     * Note that this method sets the default fetch direction for result sets
     * generated by this <code>Statement</code> object. Each result set has its
     * own methods for getting and setting its own fetch direction.
     *
     * @param direction the initial direction for processing rows
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the given direction is not
     * one of <code>ResultSet.FETCH_FORWARD</code>,
     * <code>ResultSet.FETCH_REVERSE</code>, or
     * <code>ResultSet.FETCH_UNKNOWN</code>
     * @since 1.2
     * @see #getFetchDirection
     */
    public void setFetchDirection(int direction) throws SQLException {
        stmt.setFetchDirection(direction);
    }

    /**
     * Retrieves the direction for fetching rows from database tables that is
     * the default for result sets generated from this <code>Statement</code>
     * object. If this <code>Statement</code> object has not set a fetch
     * direction by calling the method <code>setFetchDirection</code>, the
     * return value is implementation-specific.
     *
     * @return the default fetch direction for result sets generated from this
     * <code>Statement</code> object
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @since 1.2
     * @see #setFetchDirection
     */
    public int getFetchDirection() throws SQLException {
        return stmt.getFetchDirection();
    }

    /**
     * Gives the Statement driver a hint as to the number of rows that should be
     * fetched from the database when more rows are needed for
     * <code>ResultSet</code> objects generated by this <code>Statement</code>.
     * If the value specified is zero, then the hint is ignored. The default
     * value is zero.
     *
     * @param rows the number of rows to fetch
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the condition
     * {@code rows >= 0} is not satisfied.
     * @since 1.2
     * @see #getFetchSize
     */
    public void setFetchSize(int rows) throws SQLException {
        stmt.setFetchSize(rows);
    }

    /**
     * Retrieves the number of result set rows that is the default fetch size
     * for <code>ResultSet</code> objects generated from this
     * <code>Statement</code> object. If this <code>Statement</code> object has
     * not set a fetch size by calling the method <code>setFetchSize</code>, the
     * return value is implementation-specific.
     *
     * @return the default fetch size for result sets generated from this
     * <code>Statement</code> object
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @since 1.2
     * @see #setFetchSize
     */
    public int getFetchSize() throws SQLException {
        return stmt.getFetchSize();
    }

    /**
     * Retrieves the result set concurrency for <code>ResultSet</code> objects
     * generated by this <code>Statement</code> object.
     *
     * @return either <code>ResultSet.CONCUR_READ_ONLY</code> or
     * <code>ResultSet.CONCUR_UPDATABLE</code>
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @since 1.2
     */
    public int getResultSetConcurrency() throws SQLException {
        return stmt.getResultSetConcurrency();
    }

    /**
     * Retrieves the result set type for <code>ResultSet</code> objects
     * generated by this <code>Statement</code> object.
     *
     * @return one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @since 1.2
     */
    public int getResultSetType() throws SQLException {
        return stmt.getResultSetType();
    }

    /**
     * Adds the given SQL command to the current list of commands for this
     * <code>Statement</code> object. The commands in this list can be executed
     * as a batch by calling the method <code>executeBatch</code>.
     * <P>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql typically this is a SQL <code>INSERT</code> or
     * <code>UPDATE</code> statement
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the driver does not support
     * batch updates, the method is called on a <code>PreparedStatement</code>
     * or <code>CallableStatement</code>
     * @see #executeBatch
     * @see DatabaseMetaData#supportsBatchUpdates
     * @since 1.2
     */
    public void addBatch(String sql) throws SQLException {
        stmt.addBatch(sql);
    }

    /**
     * Empties this <code>Statement</code> object's current list of SQL
     * commands.
     * <P>
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the driver does not support
     * batch updates
     * @see #addBatch
     * @see DatabaseMetaData#supportsBatchUpdates
     * @since 1.2
     */
    public void clearBatch() throws SQLException {
        stmt.clearBatch();
    }

    /**
     * Submits a batch of commands to the database for execution and if all
     * commands execute successfully, returns an array of update counts. The
     * <code>int</code> elements of the array that is returned are ordered to
     * correspond to the commands in the batch, which are ordered according to
     * the order in which they were added to the batch. The elements in the
     * array returned by the method <code>executeBatch</code> may be one of the
     * following:
     * <OL>
     * <LI>A number greater than or equal to zero -- indicates that the command
     * was processed successfully and is an update count giving the number of
     * rows in the database that were affected by the command's execution
     * <LI>A value of <code>SUCCESS_NO_INFO</code> -- indicates that the command
     * was processed successfully but that the number of rows affected is
     * unknown
     * <P>
     * If one of the commands in a batch update fails to execute properly, this
     * method throws a <code>BatchUpdateException</code>, and a Statement driver
     * may or may not continue to process the remaining commands in the batch.
     * However, the driver's behavior must be consistent with a particular DBMS,
     * either always continuing to process commands or never continuing to
     * process commands. If the driver continues processing after a failure, the
     * array returned by the method
     * <code>BatchUpdateException.getUpdateCounts</code> will contain as many
     * elements as there are commands in the batch, and at least one of the
     * elements will be the following:
     *
     * <LI>A value of <code>EXECUTE_FAILED</code> -- indicates that the command
     * failed to execute successfully and occurs only if a driver continues to
     * process commands after a command fails
     * </OL>
     * <P>
     * The possible implementations and return values have been modified in the
     * Java 2 SDK, Standard Edition, version 1.3 to accommodate the option of
     * continuing to process commands in a batch update after a
     * <code>BatchUpdateException</code> object has been thrown.
     *
     * @return an array of update counts containing one element for each command
     * in the batch. The elements of the array are ordered according to the
     * order in which commands were added to the batch.
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the driver does not support
     * batch statements. Throws {@link BatchUpdateException} (a subclass of
     * <code>SQLException</code>) if one of the commands sent to the database
     * fails to execute properly or attempts to return a result set.
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     *
     * @see #addBatch
     * @see DatabaseMetaData#supportsBatchUpdates
     * @since 1.2
     */
    public int[] executeBatch() throws SQLException {
        return stmt.executeBatch();
    }

    /**
     * Retrieves the <code>Connection</code> object that produced this
     * <code>Statement</code> object.
     *
     * @return the connection that produced this statement
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @since 1.2
     */
    public String getConnection() throws SQLException {
        return conString;
    }

    //--------------------------Statement 3.0-----------------------------
    /**
     * The constant indicating that the current <code>ResultSet</code> object
     * should be closed when calling <code>getMoreResults</code>.
     *
     * @since 1.4
     */
    public int CLOSE_CURRENT_RESULT = 1;

    /**
     * The constant indicating that the current <code>ResultSet</code> object
     * should not be closed when calling <code>getMoreResults</code>.
     *
     * @since 1.4
     */
    public int KEEP_CURRENT_RESULT = 2;

    /**
     * The constant indicating that all <code>ResultSet</code> objects that have
     * previously been kept open should be closed when calling
     * <code>getMoreResults</code>.
     *
     * @since 1.4
     */
    public int CLOSE_ALL_RESULTS = 3;

    /**
     * The constant indicating that a batch statement executed successfully but
     * that no count of the number of rows it affected is available.
     *
     * @since 1.4
     */
    public int SUCCESS_NO_INFO = -2;

    /**
     * The constant indicating that an error occurred while executing a batch
     * statement.
     *
     * @since 1.4
     */
    public int EXECUTE_FAILED = -3;

    /**
     * The constant indicating that generated keys should be made available for
     * retrieval.
     *
     * @since 1.4
     */
    public int RETURN_GENERATED_KEYS = 1;

    /**
     * The constant indicating that generated keys should not be made available
     * for retrieval.
     *
     * @since 1.4
     */
    public int NO_GENERATED_KEYS = 2;

    /**
     * Moves to this <code>Statement</code> object's next result, deals with any
     * current <code>ResultSet</code> object(s) according to the instructions
     * specified by the given flag, and returns <code>true</code> if the next
     * result is a <code>ResultSet</code> object.
     *
     * <P>
     * There are no more results when the following is true:
     * <PRE>{@code
     *     // stmt is a Statement object
     *     ((stmt.getMoreResults(current) == false) && (stmt.getUpdateCount() == -1))
     * }</PRE>
     *
     * @param current one of the following <code>Statement</code> constants
     * indicating what should happen to current <code>ResultSet</code> objects
     * obtained using the method <code>getResultSet</code>:
     * <code>Statement.CLOSE_CURRENT_RESULT</code>,
     * <code>Statement.KEEP_CURRENT_RESULT</code>, or
     * <code>Statement.CLOSE_ALL_RESULTS</code>
     * @return <code>true</code> if the next result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no more
     * results
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the argument supplied is not
     * one of the following: <code>Statement.CLOSE_CURRENT_RESULT</code>,
     * <code>Statement.KEEP_CURRENT_RESULT</code> or
     * <code>Statement.CLOSE_ALL_RESULTS</code>
     * @exception SQLFeatureNotSupportedException if
     * <code>DatabaseMetaData.supportsMultipleOpenResults</code> returns
     * <code>false</code> and either <code>Statement.KEEP_CURRENT_RESULT</code>
     * or <code>Statement.CLOSE_ALL_RESULTS</code> are supplied as the argument.
     * @since 1.4
     * @see #execute
     */
    public boolean getMoreResults(int current) throws SQLException {
        return stmt.getMoreResults(current);
    }

    /**
     * Retrieves any auto-generated keys created as a result of executing this
     * <code>Statement</code> object. If this <code>Statement</code> object did
     * not generate any keys, an empty <code>ResultSet</code> object is
     * returned.
     *
     * <p>
     * <B>Note:</B>If the columns which represent the auto-generated keys were
     * not specified, the Statement driver implementation will determine the
     * columns which best represent the auto-generated keys.
     *
     * @return a <code>ResultSet</code> object containing the auto-generated
     * key(s) generated by the execution of this <code>Statement</code> object
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @since 1.4
     */
    public org.dbcloud.jdbc.ResultSet getGeneratedKeys() throws SQLException {
        org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
        convResultSet(stmt.getGeneratedKeys(), result, new Document());
        return result;
    }

    public org.dbcloud.jdbc.ResultSet getGeneratedKeys(Document options) throws SQLException {
        org.dbcloud.jdbc.ResultSet result = new org.dbcloud.jdbc.ResultSet();
        convResultSet(stmt.getGeneratedKeys(), result, options);
        return result;
    }

    /**
     * Executes the given SQL statement and signals the driver with the given
     * flag about whether the auto-generated keys produced by this
     * <code>Statement</code> object should be made available for retrieval. The
     * driver will ignore the flag if the SQL statement is not an
     * <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @param autoGeneratedKeys a flag indicating whether auto-generated keys
     * should be made available for retrieval; one of the following constants:
     * <code>Statement.RETURN_GENERATED_KEYS</code>
     * <code>Statement.NO_GENERATED_KEYS</code>
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the given SQL statement
     * returns a <code>ResultSet</code> object, the given constant is not one of
     * those allowed, the method is called on a <code>PreparedStatement</code>
     * or <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method with a constant of Statement.RETURN_GENERATED_KEYS
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.4
     */
    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        return stmt.executeUpdate(sql, autoGeneratedKeys);
    }

    /**
     * Executes the given SQL statement and signals the driver that the
     * auto-generated keys indicated in the given array should be made available
     * for retrieval. This array contains the indexes of the columns in the
     * target table that contain the auto-generated keys that should be made
     * available. The driver will ignore the array if the SQL statement is not
     * an <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @param columnIndexes an array of column indexes indicating the columns
     * that should be returned from the inserted row
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the SQL statement returns a
     * <code>ResultSet</code> object,the second argument supplied to this method
     * is not an <code>int</code> array whose elements are valid column indexes,
     * the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @since 1.4
     */
    public int executeUpdate(String sql, int columnIndexes[]) throws SQLException {
        return stmt.executeUpdate(sql, columnIndexes);
    }

    /**
     * Executes the given SQL statement and signals the driver that the
     * auto-generated keys indicated in the given array should be made available
     * for retrieval. This array contains the names of the columns in the target
     * table that contain the auto-generated keys that should be made available.
     * The driver will ignore the array if the SQL statement is not an
     * <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     * @param columnNames an array of the names of the columns that should be
     * returned from the inserted row
     * @return either the row count for <code>INSERT</code>,
     * <code>UPDATE</code>, or <code>DELETE</code> statements, or 0 for SQL
     * statements that return nothing
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the SQL statement returns a
     * <code>ResultSet</code> object, the second argument supplied to this
     * method is not a <code>String</code> array whose elements are valid column
     * names, the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.4
     */
    public int executeUpdate(String sql, String columnNames[]) throws SQLException {
        return stmt.executeUpdate(sql, columnNames);
    }

    /**
     * Executes the given SQL statement, which may return multiple results, and
     * signals the driver that any auto-generated keys should be made available
     * for retrieval. The driver will ignore this signal if the SQL statement is
     * not an <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <P>
     * In some (uncommon) situations, a single SQL statement may return multiple
     * result sets and/or update counts. Normally you can ignore this unless you
     * are (1) executing a stored procedure that you know may return multiple
     * results or (2) you are dynamically executing an unknown SQL string.
     * <P>
     * The <code>execute</code> method executes an SQL statement and indicates
     * the form of the first result. You must then use the methods
     * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve the
     * result, and <code>getMoreResults</code> to move to any subsequent
     * result(s).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql any SQL statement
     * @param autoGeneratedKeys a constant indicating whether auto-generated
     * keys should be made available for retrieval using the method
     * <code>getGeneratedKeys</code>; one of the following constants:
     * <code>Statement.RETURN_GENERATED_KEYS</code> or
     * <code>Statement.NO_GENERATED_KEYS</code>
     * @return <code>true</code> if the first result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no
     * results
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the second parameter supplied
     * to this method is not <code>Statement.RETURN_GENERATED_KEYS</code> or
     * <code>Statement.NO_GENERATED_KEYS</code>, the method is called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method with a constant of Statement.RETURN_GENERATED_KEYS
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @see #getResultSet
     * @see #getUpdateCount
     * @see #getMoreResults
     * @see #getGeneratedKeys
     *
     * @since 1.4
     */
    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        return stmt.execute(sql, autoGeneratedKeys);
    }

    /**
     * Executes the given SQL statement, which may return multiple results, and
     * signals the driver that the auto-generated keys indicated in the given
     * array should be made available for retrieval. This array contains the
     * indexes of the columns in the target table that contain the
     * auto-generated keys that should be made available. The driver will ignore
     * the array if the SQL statement is not an <code>INSERT</code> statement,
     * or an SQL statement able to return auto-generated keys (the list of such
     * statements is vendor-specific).
     * <P>
     * Under some (uncommon) situations, a single SQL statement may return
     * multiple result sets and/or update counts. Normally you can ignore this
     * unless you are (1) executing a stored procedure that you know may return
     * multiple results or (2) you are dynamically executing an unknown SQL
     * string.
     * <P>
     * The <code>execute</code> method executes an SQL statement and indicates
     * the form of the first result. You must then use the methods
     * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve the
     * result, and <code>getMoreResults</code> to move to any subsequent
     * result(s).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql any SQL statement
     * @param columnIndexes an array of the indexes of the columns in the
     * inserted row that should be made available for retrieval by a call to the
     * method <code>getGeneratedKeys</code>
     * @return <code>true</code> if the first result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no
     * results
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the elements in the
     * <code>int</code> array passed to this method are not valid column
     * indexes, the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @see #getResultSet
     * @see #getUpdateCount
     * @see #getMoreResults
     *
     * @since 1.4
     */
    public boolean execute(String sql, int columnIndexes[]) throws SQLException {
        return stmt.execute(sql, columnIndexes);
    }

    /**
     * Executes the given SQL statement, which may return multiple results, and
     * signals the driver that the auto-generated keys indicated in the given
     * array should be made available for retrieval. This array contains the
     * names of the columns in the target table that contain the auto-generated
     * keys that should be made available. The driver will ignore the array if
     * the SQL statement is not an <code>INSERT</code> statement, or an SQL
     * statement able to return auto-generated keys (the list of such statements
     * is vendor-specific).
     * <P>
     * In some (uncommon) situations, a single SQL statement may return multiple
     * result sets and/or update counts. Normally you can ignore this unless you
     * are (1) executing a stored procedure that you know may return multiple
     * results or (2) you are dynamically executing an unknown SQL string.
     * <P>
     * The <code>execute</code> method executes an SQL statement and indicates
     * the form of the first result. You must then use the methods
     * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve the
     * result, and <code>getMoreResults</code> to move to any subsequent
     * result(s).
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     *
     * @param sql any SQL statement
     * @param columnNames an array of the names of the columns in the inserted
     * row that should be made available for retrieval by a call to the method
     * <code>getGeneratedKeys</code>
     * @return <code>true</code> if the next result is a <code>ResultSet</code>
     * object; <code>false</code> if it is an update count or there are no more
     * results
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>,the elements of the
     * <code>String</code> array passed to this method are not valid column
     * names, the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @see #getResultSet
     * @see #getUpdateCount
     * @see #getMoreResults
     * @see #getGeneratedKeys
     *
     * @since 1.4
     */
    public boolean execute(String sql, String columnNames[]) throws SQLException {
        return stmt.execute(sql, columnNames);
    }

    /**
     * Retrieves the result set holdability for <code>ResultSet</code> objects
     * generated by this <code>Statement</code> object.
     *
     * @return either <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     *
     * @since 1.4
     */
    public int getResultSetHoldability() throws SQLException {
        return stmt.getResultSetHoldability();
    }

    /**
     * Retrieves whether this <code>Statement</code> object has been closed. A
     * <code>Statement</code> is closed if the method close has been called on
     * it, or if it is automatically closed.
     *
     * @return true if this <code>Statement</code> object is closed; false if it
     * is still open
     * @throws SQLException if a database access error occurs
     * @since 1.6
     */
    public boolean isClosed() throws SQLException {
        return stmt.isClosed();
    }

    /**
     * Requests that a <code>Statement</code> be pooled or not pooled. The value
     * specified is a hint to the statement pool implementation indicating
     * whether the application wants the statement to be pooled. It is up to the
     * statement pool manager as to whether the hint is used.
     * <p>
     * The poolable value of a statement is applicable to both internal
     * statement caches implemented by the driver and external statement caches
     * implemented by application servers and other applications.
     * <p>
     * By default, a <code>Statement</code> is not poolable when created, and a
     * <code>PreparedStatement</code> and <code>CallableStatement</code> are
     * poolable when created.
     * <p>
     * @param poolable requests that the statement be pooled if true and that
     * the statement not be pooled if false
     * <p>
     * @throws SQLException if this method is called on a closed
     * <code>Statement</code>
     * <p>
     * @since 1.6
     */
    public void setPoolable(boolean poolable)
            throws SQLException {
        stmt.setPoolable(poolable);
    }

    /**
     * Returns a value indicating whether the <code>Statement</code> is poolable
     * or not.
     * <p>
     * @return              <code>true</code> if the <code>Statement</code> is poolable;
     * <code>false</code> otherwise
     * <p>
     * @throws SQLException if this method is called on a closed
     * <code>Statement</code>
     * <p>
     * @since 1.6
     * <p>
     * @see java.sql.Statement#setPoolable(boolean) setPoolable(boolean)
     */
    public boolean isPoolable()
            throws SQLException {
        return stmt.isPoolable();
    }

    //--------------------------Statement 4.1 -----------------------------
    /**
     * Specifies that this {@code Statement} will be closed when all its
     * dependent result sets are closed. If execution of the {@code Statement}
     * does not produce any result sets, this method has no effect.
     * <p>
     * <strong>Note:</strong> Multiple calls to {@code closeOnCompletion} do not
     * toggle the effect on this {@code Statement}. However, a call to
     * {@code closeOnCompletion} does effect both the subsequent execution of
     * statements, and statements that currently have open, dependent, result
     * sets.
     *
     * @throws SQLException if this method is called on a closed
     * {@code Statement}
     * @since 1.7
     */
    public void closeOnCompletion() throws SQLException {
        stmt.closeOnCompletion();
    }

    /**
     * Returns a value indicating whether this {@code Statement} will be closed
     * when all its dependent result sets are closed.
     *
     * @return {@code true} if the {@code Statement} will be closed when all of
     * its dependent result sets are closed; {@code false} otherwise
     * @throws SQLException if this method is called on a closed
     * {@code Statement}
     * @since 1.7
     */
    public boolean isCloseOnCompletion() throws SQLException {
        return stmt.isCloseOnCompletion();
    }

    //--------------------------Statement 4.2 -----------------------------
    /**
     * Retrieves the current result as an update count; if the result is a
     * <code>ResultSet</code> object or there are no more results, -1 is
     * returned. This method should be called only once per result.
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * The default implementation will throw
     * {@code UnsupportedOperationException}
     *
     * @return the current result as an update count; -1 if the current result
     * is a <code>ResultSet</code> object or there are no more results
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #execute
     * @since 1.8
     */
    public long getLargeUpdateCount() throws SQLException {
        return stmt.getLargeUpdateCount();
    }

    /**
     * Sets the limit for the maximum number of rows that any
     * <code>ResultSet</code> object generated by this <code>Statement</code>
     * object can contain to the given number. If the limit is exceeded, the
     * excess rows are silently dropped.
     * <p>
     * This method should be used when the row limit may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * The default implementation will throw
     * {@code UnsupportedOperationException}
     *
     * @param max the new max rows limit; zero means there is no limit
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the condition
     * {@code max >= 0} is not satisfied
     * @see #getMaxRows
     * @since 1.8
     */
    public void setLargeMaxRows(long max) throws SQLException {
        stmt.setLargeMaxRows(max);
    }

    /**
     * Retrieves the maximum number of rows that a <code>ResultSet</code> object
     * produced by this <code>Statement</code> object can contain. If this limit
     * is exceeded, the excess rows are silently dropped.
     * <p>
     * This method should be used when the returned row limit may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * The default implementation will return {@code 0}
     *
     * @return the current maximum number of rows for a <code>ResultSet</code>
     * object produced by this <code>Statement</code> object; zero means there
     * is no limit
     * @exception SQLException if a database access error occurs or this method
     * is called on a closed <code>Statement</code>
     * @see #setMaxRows
     * @since 1.8
     */
    public long getLargeMaxRows() throws SQLException {
        return stmt.getLargeMaxRows();
    }

    /**
     * Submits a batch of commands to the database for execution and if all
     * commands execute successfully, returns an array of update counts. The
     * <code>long</code> elements of the array that is returned are ordered to
     * correspond to the commands in the batch, which are ordered according to
     * the order in which they were added to the batch. The elements in the
     * array returned by the method {@code executeLargeBatch} may be one of the
     * following:
     * <OL>
     * <LI>A number greater than or equal to zero -- indicates that the command
     * was processed successfully and is an update count giving the number of
     * rows in the database that were affected by the command's execution
     * <LI>A value of <code>SUCCESS_NO_INFO</code> -- indicates that the command
     * was processed successfully but that the number of rows affected is
     * unknown
     * <P>
     * If one of the commands in a batch update fails to execute properly, this
     * method throws a <code>BatchUpdateException</code>, and a Statement driver
     * may or may not continue to process the remaining commands in the batch.
     * However, the driver's behavior must be consistent with a particular DBMS,
     * either always continuing to process commands or never continuing to
     * process commands. If the driver continues processing after a failure, the
     * array returned by the method
     * <code>BatchUpdateException.getLargeUpdateCounts</code> will contain as
     * many elements as there are commands in the batch, and at least one of the
     * elements will be the following:
     *
     * <LI>A value of <code>EXECUTE_FAILED</code> -- indicates that the command
     * failed to execute successfully and occurs only if a driver continues to
     * process commands after a command fails
     * </OL>
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * The default implementation will throw
     * {@code UnsupportedOperationException}
     *
     * @return an array of update counts containing one element for each command
     * in the batch. The elements of the array are ordered according to the
     * order in which commands were added to the batch.
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code> or the driver does not support
     * batch statements. Throws {@link BatchUpdateException} (a subclass of
     * <code>SQLException</code>) if one of the commands sent to the database
     * fails to execute properly or attempts to return a result set.
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     *
     * @see #addBatch
     * @see DatabaseMetaData#supportsBatchUpdates
     * @since 1.8
     */
    public long[] executeLargeBatch() throws SQLException {
        return stmt.executeLargeBatch();
    }

    /**
     * Executes the given SQL statement, which may be an <code>INSERT</code>,
     * <code>UPDATE</code>, or <code>DELETE</code> statement or an SQL statement
     * that returns nothing, such as an SQL DDL statement.
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     * <p>
     * The default implementation will throw
     * {@code UnsupportedOperationException}
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the given SQL statement
     * produces a <code>ResultSet</code> object, the method is called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.8
     */
    public long executeLargeUpdate(String sql) throws SQLException {
        return stmt.executeLargeUpdate(sql);
    }

    /**
     * Executes the given SQL statement and signals the driver with the given
     * flag about whether the auto-generated keys produced by this
     * <code>Statement</code> object should be made available for retrieval. The
     * driver will ignore the flag if the SQL statement is not an
     * <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     * <p>
     * The default implementation will throw
     * {@code SQLFeatureNotSupportedException}
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @param autoGeneratedKeys a flag indicating whether auto-generated keys
     * should be made available for retrieval; one of the following constants:
     * <code>Statement.RETURN_GENERATED_KEYS</code>
     * <code>Statement.NO_GENERATED_KEYS</code>
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the given SQL statement
     * returns a <code>ResultSet</code> object, the given constant is not one of
     * those allowed, the method is called on a <code>PreparedStatement</code>
     * or <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method with a constant of Statement.RETURN_GENERATED_KEYS
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.8
     */
    public long executeLargeUpdate(String sql, int autoGeneratedKeys)
            throws SQLException {
        return stmt.executeLargeUpdate(sql, autoGeneratedKeys);
    }

    /**
     * Executes the given SQL statement and signals the driver that the
     * auto-generated keys indicated in the given array should be made available
     * for retrieval. This array contains the indexes of the columns in the
     * target table that contain the auto-generated keys that should be made
     * available. The driver will ignore the array if the SQL statement is not
     * an <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     * <p>
     * The default implementation will throw
     * {@code SQLFeatureNotSupportedException}
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     *
     * @param columnIndexes an array of column indexes indicating the columns
     * that should be returned from the inserted row
     * @return either (1) the row count for SQL Data Manipulation Language (DML)
     * statements or (2) 0 for SQL statements that return nothing
     *
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the SQL statement returns a
     * <code>ResultSet</code> object,the second argument supplied to this method
     * is not an <code>int</code> array whose elements are valid column indexes,
     * the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.8
     */
    public long executeLargeUpdate(String sql, int columnIndexes[]) throws SQLException {
        return stmt.executeLargeUpdate(sql, columnIndexes);
    }

    /**
     * Executes the given SQL statement and signals the driver that the
     * auto-generated keys indicated in the given array should be made available
     * for retrieval. This array contains the names of the columns in the target
     * table that contain the auto-generated keys that should be made available.
     * The driver will ignore the array if the SQL statement is not an
     * <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <p>
     * This method should be used when the returned row count may exceed
     * {@link Integer#MAX_VALUE}.
     * <p>
     * <strong>Note:</strong>This method cannot be called on a
     * <code>PreparedStatement</code> or <code>CallableStatement</code>.
     * <p>
     * The default implementation will throw
     * {@code SQLFeatureNotSupportedException}
     *
     * @param sql an SQL Data Manipulation Language (DML) statement, such as
     * <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code>; or an
     * SQL statement that returns nothing, such as a DDL statement.
     * @param columnNames an array of the names of the columns that should be
     * returned from the inserted row
     * @return either the row count for <code>INSERT</code>,
     * <code>UPDATE</code>, or <code>DELETE</code> statements, or 0 for SQL
     * statements that return nothing
     * @exception SQLException if a database access error occurs, this method is
     * called on a closed <code>Statement</code>, the SQL statement returns a
     * <code>ResultSet</code> object, the second argument supplied to this
     * method is not a <code>String</code> array whose elements are valid column
     * names, the method is called on a <code>PreparedStatement</code> or
     * <code>CallableStatement</code>
     * @throws SQLFeatureNotSupportedException if the Statement driver does not
     * support this method
     * @throws SQLTimeoutException when the driver has determined that the
     * timeout value that was specified by the {@code setQueryTimeout} method
     * has been exceeded and has at least attempted to cancel the currently
     * running {@code Statement}
     * @since 1.8
     */
    public long executeLargeUpdate(String sql, String columnNames[])
            throws SQLException {
        return stmt.executeLargeUpdate(sql, columnNames);
    }

    private void buildSqlWhere(StringBuilder whereBuilder, String condition) {
        if (whereBuilder.length() == 0) {
            whereBuilder.append(" Where ").append(condition);
        } else {
            whereBuilder.append(" And ").append(condition);
        }
    }

    private String buildSqlIn(String strTextWithSpliter, boolean needAddSingleQuote) {

        String[] temp = strTextWithSpliter.split(",");
        StringBuilder sb = new StringBuilder();
        for (String t : temp) {
            if (t.trim().equals("")) {
                continue;
            }
            if (sb.length() == 0) {
                if (needAddSingleQuote) {
                    sb.append(" In('").append(t).append("'");
                } else {
                    sb.append(" In(").append(t);
                }
            } else {
                if (needAddSingleQuote) {
                    sb.append(" ,'").append(t).append("'");
                } else {
                    sb.append(" ,").append(t);
                }
            }
        }
        if (sb.length() > 0) {
            sb.append(")");
        }
        return sb.toString();
    }
}
